/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.hermes.ssoLogin.saml.web;

import com.google.common.base.Preconditions;
import com.hyts.hermes.ssoLogin.saml.config.OktaSamlParameter;
import com.hyts.hermes.ssoLogin.saml.service.DefaultOktaClientLoginService;
import com.hyts.hermes.ssoLogin.saml.service.DefaultOktaClientSSOService;
import com.hyts.hermes.ssoLogin.saml.service.OktaClientLoginService;
import com.onelogin.saml2.Auth;
import lombok.extern.slf4j.Slf4j;
import org.opensaml.xml.signature.SignatureValidator;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.security.cert.X509Certificate;

/**
 * @project-name:hermes
 * @package-name:com.hyts.hermes.ssoLogin.saml
 * @author:LiBo/Alex
 * @create-date:2023-04-07 15:36
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */
@Slf4j
@RestController
@RequestMapping("/okta")
public class OktaClientLoginController {


    @Autowired
    private ObjectProvider<OktaClientLoginService> oktaLoginServices;

    @Autowired
    private DefaultOktaClientSSOService defaultOktaClientSSOService;


    private DefaultOktaClientLoginService emptyOktaLoginService = new DefaultOktaClientLoginService() ;



    /**
     * ”一键登录“按钮，触发request接口，即可跳转至Okta进行验证登录
     *
     * @param request
     * @param response
     */
    @GetMapping("/preLogin")
    public void preLogin(HttpServletRequest request, HttpServletResponse response) {
        Auth auth = null;
        try {
            log.info("[hermes-ssoLogin] - receive the access okta sso login request.");
            OktaSamlParameter oktaSamlParameter = defaultOktaClientSSOService.getOktaSamlPropertiesModel(null);
            auth = defaultOktaClientSSOService.createAuthEntity(request,response, oktaSamlParameter);
            auth.login();
            log.info("[hermes-ssoLogin] - redirect the third-party-server by okta login page or check the okta session.");
        } catch (Exception e) {
            log.error("[hermes-ssoLogin] - access the okta system is failure!", e);
        }
    }

    /**
     * 在Okta登录完毕后，okta会回调callback地址，这个地址是在Okta applications中自己设置的 Single Sign On URL
     *
     * @param responseMsg
     * @param httpServletRequest
     * @param httpServletResponse
     * @throws IOException
     */
    @PostMapping("/doLogin")
    public void doLogin(@RequestParam("SAMLResponse") String responseMsg, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        try {
            //从回调中获取账号信息
            log.info("[hermes-ssoLogin] - get the okta platform callback message:{}",
                    responseMsg);
            OktaSamlParameter oktaSamlParameter = defaultOktaClientSSOService.getOktaSamlPropertiesModel(null);
            Auth auth = defaultOktaClientSSOService.createAuthEntity(httpServletRequest,httpServletResponse, oktaSamlParameter);
            // 获取对应的x509Certificate的证书信息
            //x509cert 配置
            X509Certificate x509Certificate = auth.getSettings().getIdpx509cert();
            Preconditions.checkNotNull(x509Certificate,
                    "[hermes-doLogin] not get or config the [idp-x509-cert] value.");
            // 检测证书是否已经过期！
            x509Certificate.checkValidity();
            String onelogin_idp_cert = oktaSamlParameter.getIdpX509cert();
            // 单点登录检验对应的idp的加密操作控制服务
            SignatureValidator signatureValidator = defaultOktaClientSSOService.decryptSaml2CallBackIdpx509ToSignatureValidator(onelogin_idp_cert);
            Preconditions.checkNotNull(signatureValidator,
                    "[hermes-doLogin] decryptSaml2CallBackIdpx509ToSignatureValidator is failure.");
            // 获取对应的命名id/核心id
            DefaultOktaClientSSOService.UserModel userModel = defaultOktaClientSSOService.validateAndAnalysisSaml2EntityForLogin(responseMsg, signatureValidator);
            Preconditions.checkNotNull(userModel.getUserName(),
                    "[hermes-doLogin] not get the nameId by validateAndAnalysisSaml2EntityForLogin.");
            //通过账号进行免密登录，并授权token / 跳转登录页面
            String token = oktaLoginServices.getIfAvailable(() ->emptyOktaLoginService).login(userModel,httpServletRequest,
                    httpServletResponse, oktaSamlParameter.getSpAssertionConsumerServiceUrl());
            log.info("[hermes-doLogin] finished the doLogin process workflow - token:{}",token);
        } catch (Exception e) {
            log.error("[hermes-doLogin] process the okta system callback the current System is failure", e);
        }
    }

    /**
     * 预登出操作
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("/preLogout")
    public void logout(@RequestParam("userName") String userName,HttpServletRequest request, HttpServletResponse response) throws IOException {
        Auth auth = null;
        try {
            OktaSamlParameter oktaSamlParameter = defaultOktaClientSSOService.getOktaSamlPropertiesModel(null);
            auth = defaultOktaClientSSOService.createAuthEntity(request,response, oktaSamlParameter);
            HttpSession session = request.getSession();
            String nameId = null;
            if (session.getAttribute("nameId") != null) {
                nameId = session.getAttribute("nameId").toString();
            }else{
                nameId = userName;
            }
            String nameIdFormat = null;
            if (session.getAttribute("nameIdFormat") != null) {
                nameIdFormat = session.getAttribute("nameIdFormat").toString();
            }
            String nameidNameQualifier = null;
            if (session.getAttribute("nameidNameQualifier") != null) {
                nameidNameQualifier = session.getAttribute("nameidNameQualifier").toString();
            }
            String nameidSPNameQualifier = null;
            if (session.getAttribute("nameidSPNameQualifier") != null) {
                nameidSPNameQualifier = session.getAttribute("nameidSPNameQualifier").toString();
            }
            String sessionIndex = null;
            if (session.getAttribute("sessionIndex") != null) {
                sessionIndex = session.getAttribute("sessionIndex").toString();
            }
            auth.logout(null, nameId, sessionIndex, nameIdFormat, nameidNameQualifier, nameidSPNameQualifier);
        } catch (Exception e) {
            log.error("[hermes-preLogout] execute the logout is failure.", e);
        }
    }

    /**
     * 执行登出操作
     * @param request
     * @param response
     * @throws IOException
     */
    @PostMapping("/doLogout")
    public void doLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {
        try {
            OktaSamlParameter oktaSamlParameter = defaultOktaClientSSOService.getOktaSamlPropertiesModel(null);
            //okta 回调参数
            String responseMsg = request.getParameter("SAMLResponse");
            Auth auth = defaultOktaClientSSOService.createAuthEntity(request,response, oktaSamlParameter);
            //x509cert 配置
            X509Certificate x509Certificate = auth.getSettings().getIdpx509cert();
            Preconditions.checkNotNull(x509Certificate,
                    "[hermes-doLogout] not get or config the [idp-x509-cert] value.");
            // 检测证书是否已经过期！
            x509Certificate.checkValidity();
            String onelogin_idp_cert = oktaSamlParameter.getIdpX509cert();
            // 单点登录检验对应的idp的加密操作控制服务
            SignatureValidator signatureValidator = defaultOktaClientSSOService.decryptSaml2CallBackIdpx509ToSignatureValidator(onelogin_idp_cert);
            //从回调中获取账号信息
            DefaultOktaClientSSOService.UserModel userModel = defaultOktaClientSSOService.validateAndAnalysisSaml2EntityForLogout(responseMsg,signatureValidator);
            //通过账号进行免密登录，并授权token  这里的登录实现是伪代码，懂意思
            Boolean result = oktaLoginServices.getIfAvailable(() ->emptyOktaLoginService).logout(userModel,request,response,
                    oktaSamlParameter.getIdpSingleLogoutServiceUrl());
            log.info("[hermes-doLogout] finished the doLogin process workflow - result:{}",result);
        } catch (Exception e) {
            log.error("[hermes-doLogout] process the okta system callback the current System is failure", e);
        }
    }

    /**
     * 获取元数据信息
     * @param request
     * @param response
     */
    @RequestMapping("/metadata")
    public void metadata(HttpServletRequest request, HttpServletResponse response){
        try {
            OktaSamlParameter oktaSamlParameter = defaultOktaClientSSOService.getOktaSamlPropertiesModel(null);
            Auth auth = defaultOktaClientSSOService.createAuthEntity(request,response, oktaSamlParameter);
            log.info("open the metadata api：{}",auth.getSettings().getSPMetadata());
        } catch (Exception e) {
            log.error("occur the exception in metadata api!",e);
        }
    }
}
